<?php

namespace addons\yungift\library;

use addons\yungift\model\Config;
use addons\yungift\model\GiftMain;
use addons\yungift\model\GiftOrder;
use addons\yungift\model\SmsRecord;
use addons\yungift\model\UserAuth;
use fast\Http;
use think\Cache;
use think\Exception;
use think\Paginator;
use think\Env;

class Logic
{
    // 资源类别
    const SOURCE_GIFT        = 'gift';
    const SOURCE_GIFT_RECORD = 'gift_order';

    public static function getSourceList()
    {
        return [
            self::SOURCE_GIFT        => '礼品',
            self::SOURCE_GIFT_RECORD => '礼品订单',
        ];
    }

    /**
     * @param $openid
     * @param $source
     * @return array|bool|float|int|mixed|object|\stdClass|null
     * @throws \think\exception\DbException
     */
    public static function getUserAuthKey($openid, $source)
    {
        if (! $userAuth = UserAuth::get(['openid' => $openid, 'source' => $source])) {
            $userAuth = new UserAuth();
            $userAuth->source = $source;
            $userAuth->openid = $openid;
            $userAuth->auth_key = md5(uniqid(rand(), true));
            $userAuth->auth_key_expire = time() + 86400 * 7;
            $userAuth->user_id = 0;
            $userAuth->save();
        } elseif ($userAuth->auth_key_expire < time() + 300) {
            $userAuth->auth_key = md5(uniqid(rand(), true));
            $userAuth->auth_key_expire = time() + 86400 * 7;
            $userAuth->save();
        }

        return $userAuth->auth_key;
    }

    /**
     * @param $authKey
     * @param $source
     * @return array|bool|float|int|mixed|object|\stdClass|null
     * @throws \think\exception\DbException
     */
    public static function getUserAuthOpenid($authKey, $source)
    {
        $userAuth = UserAuth::get(['auth_key' => $authKey, 'source' => $source]);
        if ($userAuth && $userAuth->auth_key_expire > time()) {
            return $userAuth->openid;
        }

        return null;
    }

    /**
     * @param $openid
     * @param $source
     * @return array|bool|float|int|mixed|object|\stdClass|null
     * @throws \think\exception\DbException
     */
    public static function getUserAuthUid($openid, $source)
    {
        if (! $openid || ! $source) {
            return 0;
        }
        if (! $userAuth = UserAuth::get(['openid' => $openid, 'source' => $source])) {
            $userAuth = new UserAuth();
            $userAuth->source = $source;
            $userAuth->openid = $openid;
            $userAuth->user_id = 0;
            $userAuth->save();
        }

        return $userAuth->user_id;
    }

    /**
     * @param $userId
     * @param $openid
     * @param $source
     * @throws \think\exception\DbException
     */
    public static function bindUserAuth($userId, $openid, $source)
    {
        if ($openid && $source) {
            if (! $userAuth = UserAuth::get(['openid' => $openid, 'source' => $source])) {
                $userAuth = new UserAuth();
                $userAuth->source = $source;
                $userAuth->openid = $openid;
            }
            $userAuth->user_id = $userId;
            $userAuth->save();
        }
    }

    /**
     * @param $userId
     * @param $source
     */
    public static function unbindUserAuth($userId, $source)
    {
        UserAuth::update(['user_id' => 0], ['user_id' => $userId, 'source' => $source]);
    }

    /**
     * 获取小程序access_token
     *
     * @return array|false
     */
    public static function getWechatAccessToken($cacheDuration = 86400)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_BASE, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_BASE, 'app_secret');
        if (! $appId || ! $appSecret) {
            return false;
        }

        // 获取access_token
        $retry = 1;
        while (true) {
            $accessToken = Cache::remember('accessToken:' . $appId, function () use ($appId, $appSecret) {
                $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $appSecret;
                $result = json_decode(Http::get($url), true);
                if (empty($result['access_token'])) {
                    return false;
                }

                return $result['access_token'];
            }, $cacheDuration);
            if (! $accessToken) {
                Cache::rm('accessToken:' . $appId);
                if ($retry-- <= 0) {
                    return false;
                }
                continue;
            }

            return $accessToken;
        }
    }

    /**
     * 获取小程序登录code对应的openid
     *
     * @param $code
     * @return array|false
     */
    public static function getMiniCodeData($code, $throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_BASE, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_BASE, 'app_secret');
        if (! $appId || ! $appSecret) {
            if ($throw) {
                throw new Exception('小程序的AppId或AppSecret未配置');
            }

            return false;
        }
        // 获取openid
        $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appId . '&secret=' . $appSecret . '&js_code=' . $code . '&grant_type=authorization_code';
        $result = json_decode(Http::get($url), true);
        if (empty($result['openid'])) {
            if ($throw) {
                throw new Exception($result['errmsg']);
            }

            return false;
        }

        return $result;
    }

    /**
     * 获取小程序手机号code对应的信息
     *
     * @param $code
     * @return array|false
     */
    public static function getWechatPhoneData($code, $throw = true)
    {
        $retry = 1;
        while (true) {
            $accessToken = self::getWechatAccessToken($throw);

            // 获取手机信息
            $url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=' . $accessToken;
            $result = json_decode(Http::post($url, json_encode(['code' => $code])), true);
            // https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
            if (empty($result['phone_info'])) {
                if (in_array($result['errcode'], [40001, 40014, 41001, 42001])) {
                    Cache::rm('accessToken:' . Config::getGroupAttribute(Config::GROUP_BASE, 'app_id'));
                    if ($retry-- <= 0) {
                        if ($throw) {
                            throw new Exception($result['errmsg']);
                        }

                        return false;
                    }
                    continue;
                }
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return $result['phone_info'];
        }
    }

    /**
     * 获取小程序手机号code对应的信息
     *
     * @param $code
     * @return array|false
     */
    public static function getWechatQrcode($page, $scene, $options = [], $throw = true)
    {
        $retry = 1;
        while (true) {
            $accessToken = self::getWechatAccessToken($throw);

            // 获取手机信息
            $params = $options;
            $params['page'] = $page;
            $params['scene'] = $scene;
            $url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=' . $accessToken;
            $response = Http::post($url, json_encode($params));
            $result = json_decode($response, true);
            // https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html
            if ($result) {
                if (in_array($result['errcode'], [40001, 40014, 41001, 42001])) {
                    Cache::rm('accessToken:' . Config::getGroupAttribute(Config::GROUP_BASE, 'app_id'));
                    if ($retry-- <= 0) {
                        if ($throw) {
                            throw new Exception($result['errmsg']);
                        }

                        return false;
                    }
                    continue;
                }
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return $response;
        }
    }

    /**
     * 解密小程序的加密信息
     *
     * @param $userId
     * @param $encryptedData
     * @param $iv
     * @return false|mixed
     */
    public static function decryptWechatData($sessionKey, $encryptedData, $iv)
    {
        if (! $appid = Config::getGroupAttribute(Config::GROUP_BASE, 'app_id')) {
            return false;
        }
        $wxBiz = new WXBizDataCrypt($appid, $sessionKey);
        $errCode = $wxBiz->decryptData($encryptedData, $iv, $data);
        if ($errCode != 0) {
            return false;
        }

        return json_decode($data, true);
    }

    /**
     * 获取公众号access_token
     *
     * @return array|false
     */
    public static function getMpAccessToken($throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_MP, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_MP, 'app_secret');
        if (! $appId || ! $appSecret) {
            if ($throw) {
                throw new Exception('公众号AppId或AppSecret未配置');
            }

            return false;
        }

        // 获取access_token
        return Cache::remember('mpAccessToken:' . $appId, function () use ($appId, $appSecret, $throw) {
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $appSecret;
            $result = json_decode(Http::get($url), true);
            if (empty($result['access_token'])) {
                if ($throw) {
                    throw new Exception($result['errmsg']);
                }

                return false;
            }

            return $result['access_token'];
        }, 7200);
    }

    /**
     * 获取微信公众号code对应的openid
     *
     * @param $code
     * @return array|false
     */
    public static function getMpCodeData($code, $throw = true)
    {
        $appId = Config::getGroupAttribute(Config::GROUP_MP, 'app_id');
        $appSecret = Config::getGroupAttribute(Config::GROUP_MP, 'app_secret');
        if (! $appId || ! $appSecret) {
            if ($throw) {
                throw new Exception('公众号AppId或AppSecret未配置');
            }

            return false;
        }

        // 获取openid
        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appId . '&secret=' . $appSecret . '&code=' . $code . '&grant_type=authorization_code';
        $result = json_decode(Http::get($url), true);
        if (empty($result['openid'])) {
            if ($throw) {
                throw new Exception($result['errmsg']);
            }

            return false;
        }

        return $result;
    }

    /**
     * 获取分页数据
     *
     * @param Paginator $paginator
     * @param array $fields
     * @return array
     */
    public static function getPageData(Paginator $paginator, $fields = [])
    {
        $result = $paginator->toArray();
        $items = $fields ? Super::arrayGetSet($paginator->items(), $fields, true) : $result['data'];

        return [
            'total'        => $result['total'],
            'per_page'     => $result['per_page'],
            'current_page' => $result['current_page'],
            'last_page'    => $result['last_page'],
            'items'        => $items
        ];
    }

    /**
     * 给模型设置初始值
     */
    public static function setDefaultValue(&$model, $attributes)
    {
        $data = $model->getData(null);
        foreach ($attributes as $field => $value) {
            if (! array_key_exists($field, $data)) {
                $model->$field = $value;
            }
        }
    }

    /**
     * 发送兑换信息
     *
     * @param $orderId
     * @return bool
     */
    public static function sendExchangeMessage($orderId)
    {
        $smsConfig = Config::getGroupAttributes(Config::GROUP_SMS);
        $templateCode = $smsConfig['exchange_template_code'] ?? '';
        $templateCont = $smsConfig['exchange_template_cont'] ?? '';
        $noticeScenes = $smsConfig['exchange_notice_scenes'] ?? '';
        $receivePhones = $smsConfig['exchange_receive_phones'] ?? '';
        if (! $templateCode || ! $templateCont || ! $receivePhones) {
            return false;
        }
        $noticeScenes = explode(',', $noticeScenes);
        list($order, $data) = self::getOrderData($orderId);
        if (! in_array($order->gift_type, $noticeScenes)) {
            return false;
        }
        $params = self::getSmsParams($templateCont, $data);
        $receivePhones = explode(',', str_replace([' ', '，'], ['', ','], $receivePhones));
        foreach ($receivePhones as $phone) {
            self::sendSms($phone, $templateCode, $params);
        }

        return true;
    }

    /**
     * 发送发货信息
     *
     * @param $orderId
     * @return bool
     */
    public static function sendDeployMessage($orderId)
    {
        $smsConfig = Config::getGroupAttributes(Config::GROUP_SMS);
        $templateCode = $smsConfig['deploy_template_code'] ?? '';
        $templateCont = $smsConfig['deploy_template_cont'] ?? '';
        $noticeScenes = $smsConfig['deploy_notice_scenes'] ?? '';
        if (! $templateCode || ! $templateCont || ! $noticeScenes) {
            return false;
        }
        $noticeScenes = explode(',', $noticeScenes);
        list($order, $data) = self::getOrderData($orderId);
        if ($order->gift_type != GiftMain::TYPE_1) {
            return false;
        }
        if (! in_array($order->gain_method, $noticeScenes)) {
            return false;
        }
        if (empty($order->phone)) {
            return false;
        }
        $params = self::getSmsParams($templateCont, $data);

        return self::sendSms($order->phone, $templateCode, $params);
    }


    /**
     * 给手机号发验证码
     *
     * @param $code
     * @return array|false
     */
    public static function sendPhoneCaptcha($phone, $captcha)
    {
        $templateCode = Config::getGroupAttribute(Config::GROUP_SMS, 'template_code');
        $params = ['code' => $captcha];

        return self::sendSms($phone, $templateCode, $params);
    }

    public static function getOrderData($orderId)
    {
        if (! $order = GiftOrder::get($orderId)) {
            throw new Exception('订单不存在');
        }

        $data = Super::arrayGetSet($order, [
            'orderNo'        => 'number',
            'giftName'       => function ($item) {
                $giftData = json_decode($item->gift_data, true);

                return $giftData['name'];
            },
            'giftType'       => function ($item) {
                $giftData = json_decode($item->gift_data, true);

                return $giftData['type_text'];
            },
            'gainMethod'     => 'gain_method_text',
            'userName'       => 'name',
            'userPhone'      => 'phone',
            'expressCompany' => 'express_company_text',
            'expressNumber'  => 'express_number',
            'expressPhone'   => 'express_phone',
        ]);

        return [$order, $data];
    }

    /**
     * 获取短信参数
     *
     * @param $template
     * @param $data
     * @return array
     */
    public static function getSmsParams($template, $data)
    {
        preg_match_all("/\\$\{([a-zA-Z0-9_]+)\}/", $template, $matches);
        if (empty($matches[1])) {
            return [];
        }
        $params = [];
        foreach ($data as $key => $value) {
            if (in_array($key, $matches[1])) {
                $params[$key] = $value;
            }
        }

        return $params;
    }

    /**
     * 发送短信
     *
     * @param $code
     * @return array|false
     */
    public static function sendSms($phone, $templateCode, $params)
    {
        $smsConfig = Config::getGroupAttributes(Config::GROUP_SMS);
        $accessKeyId = $smsConfig['app_key_id'] ?? '';
        $accessKeySecret = $smsConfig['app_key_secret'] ?? '';
        $signName = $smsConfig['sign_name'] ?? '';
        $error = '';
        try {
            $response = Super::sendAliyunPhoneCode($phone, $params, $templateCode, $accessKeyId, $accessKeySecret,
                $signName);
            if (! $response) {
                $error = '网络错误';
            } elseif ($response['Code'] !== 'OK') {
                $error = $response['Message'];
            }
        } catch (Exception $e) {
            $response = $e->getMessage();
            $error = $e->getMessage();
        }

        // 发送记录
        $smsRecord = new SmsRecord();
        $smsRecord->phone = $phone;
        $smsRecord->template_code = $templateCode;
        $smsRecord->params = json_encode($params);
        $smsRecord->response = json_encode($response);
        $smsRecord->save();

        return $error;
    }

    /**
     * 查询快递物流公司
     *
     * @param $appCode
     * @param $number
     * @param $company
     * @return false|mixed|string
     */
    public static function queryExpressCompany($cacheDuration = 86400)
    {
        if (! $appcode = Config::getGroupAttribute(Config::GROUP_EXPRESS, 'aliyun_appcode')) {
            return [];
        }

        return Cache::remember('globalExpressLists', function () use ($appcode) {
            $api = 'https://wdexpress.market.alicloudapi.com/globalExpressLists';
            $response = Super::requestAliCloudApi($appcode, $api, 'GET', ['type' => 'ALL']);
            $response = json_decode($response, true);

            return $response['result'];
        }, $cacheDuration);
    }

    /**
     * 查询快递物流信息
     * 注：顺丰必须传手机号
     *
     * @param $appCode
     * @param $number
     * @param $company
     * @return false|mixed|string
     */
    public static function queryExpressTrack($company, $number, $phone = '', $cacheDuration = 600)
    {
        $cacheKey = 'expressTrack:' . $company . ':' . $number . ':' . $phone;
        if (! $appcode = Config::getGroupAttribute(Config::GROUP_EXPRESS, 'aliyun_appcode')) {
            throw new Exception('阿里云物流插件appcode未配置');
        }
        if ($company === 'SF') {
            if (empty($phone)) {
                throw new Exception('顺丰必须填写手机号，才能查询物流信息');
            }
            $number .= ':' . substr($phone, -4, 4);
        }

        return Cache::remember($cacheKey, function () use ($appcode, $number, $company) {
            $api = 'https://wdexpress.market.alicloudapi.com/gxali';
            $trackInfo = Super::requestAliCloudApi($appcode, $api, 'GET', ['n' => $number, 't' => $company]);
            $trackResult = json_decode($trackInfo, true);
            $result = ['number' => $trackResult['LogisticCode']];

            // 状态
            $state = '未知状态';
            switch ($trackResult['State']) {
                case -1:
                    $state = '单号或快递公司代码错误';
                    break;
                case 0:
                    $state = '暂无轨迹';
                    break;
                case 1:
                    $state = '快递收件';
                    break;
                case 2:
                    $state = '在途中';
                    break;
                case 3:
                    $state = '已签收';
                    break;
                case 4:
                    $state = '问题件';
                    break;
                case 5:
                    $state = '疑难件（收件人拒绝签收，地址有误或不能送达派送区域，收费等原因无法正常派送）';
                    break;
                case 6:
                    $state = '退件签收';
                    break;
            }
            $result['state'] = $trackResult['State'];
            $result['state_text'] = $state;
            $result['company'] = $trackResult['ShipperCode'] ?? '未知';

            // 物流
            $trackList = [];
            foreach ($trackResult['Traces'] as $item) {
                $trackList[] = [
                    'time'    => $item['AcceptTime'],
                    'context' => $item['AcceptStation'],
                ];
            }
            $result['track_list'] = array_reverse($trackList);

            return $result;
        }, $cacheDuration);
    }

    /**
     * @param $assertion
     * @param $description
     */
    public static function niceAssert($assertion, $description = '操作失败')
    {
        if (is_string($description)) {
            assert_options(ASSERT_EXCEPTION, 1);
            $description = new Exception($description);
        }
        assert($assertion, $description);
    }

    /**
     * @param string[] $args
     * @return void
     */
    public static function sendAlisms($phone, $code){
        $accessKeyId = Env::get('sms.accessKeyId');
        $accessKeySecret = Env::get('sms.accessKeySecret');
        $signName = Env::get('sms.signName');
        $templateCode = Env::get('sms.templateCode');
        $params = ['code' => $code];

        $error = '';
        try {
            $response = Super::sendAliyunPhoneCode($phone, $params, $templateCode, $accessKeyId, $accessKeySecret,
                $signName);
            if (! $response) {
                $error = '网络错误';
            } elseif ($response['Code'] !== 'OK') {
                $error = $response['Message'];
            }
        } catch (Exception $e) {
            $response = $e->getMessage();
            $error = $e->getMessage();
        }

        // 发送记录
        $smsRecord = new SmsRecord();
        $smsRecord->phone = $phone;
        $smsRecord->template_code = $templateCode;
        $smsRecord->params = json_encode($params);
        $smsRecord->response = json_encode($response);
        $smsRecord->save();

        return $error;
    }

}
